﻿using IronBefunge.Core.InstructionHandlers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace IronBefunge.Core.Tests.InstructionHandlers
{
	[TestClass]
	public class MathInstructionHandlerTests : InstructionHandlerTests
	{
		internal override ReadOnlyCollection<char> GetExpectedHandledInstructions()
		{
			return new List<char>() { MathInstructionHandler.AddInstruction,
				MathInstructionHandler.DivideInstruction, MathInstructionHandler.ModInstruction,
				MathInstructionHandler.MultiplyInstruction, MathInstructionHandler.SubtractInstruction }.AsReadOnly();
		}

		internal override Type GetHandlerType()
		{
			return typeof(MathInstructionHandler);
		}

		[TestMethod]
		public void HandleAdd()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.AddInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
				{
					context.Values.Push(3);
					context.Values.Push(7);
					stackCount = context.Values.Count;
				}, (context, result) =>
				{
					Assert.AreEqual(stackCount - 1, context.Values.Count);
					Assert.AreEqual(10, context.Values.Peek());
				});
		}

		[TestMethod]
		public void HandleAddWithOnlyOneValueOnTheStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.AddInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
				{
					context.Values.Push(3);
					stackCount = context.Values.Count;
				}, (context, result) =>
				{
					Assert.AreEqual(stackCount, context.Values.Count);
					Assert.AreEqual(3, context.Values.Peek());
				});
		}

		[TestMethod]
		public void HandleAddWithEmptyStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.AddInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
				{
					stackCount = context.Values.Count;
				}, (context, result) =>
				{
					Assert.AreEqual(stackCount + 1, context.Values.Count);
					Assert.AreEqual(0, context.Values.Peek());
				});
		}

		[TestMethod]
		public void HandleDivide()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.DivideInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
			{
				context.Values.Push(7);
				context.Values.Push(3);
				stackCount = context.Values.Count;
			}, (context, result) =>
			{
				Assert.AreEqual(stackCount - 1, context.Values.Count);
				Assert.AreEqual(2, context.Values.Peek());
			});
		}

		[TestMethod]
		public void HandleDivideWithOnlyOneValueOnTheStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.DivideInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
			{
				context.Values.Push(3);
				stackCount = context.Values.Count;
			}, (context, result) =>
			{
				Assert.AreEqual(stackCount, context.Values.Count);
				Assert.AreEqual(0, context.Values.Peek());
			});
		}

		[TestMethod]
		public void HandleDivideWithEmptyStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.DivideInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
			{
				stackCount = context.Values.Count;
			}, (context, result) =>
			{
				Assert.AreEqual(stackCount + 1, context.Values.Count);
				Assert.AreEqual(0, context.Values.Peek());
			});
		}

		[TestMethod]
		public void HandleMod()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.ModInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
			{
				context.Values.Push(7);
				context.Values.Push(3);
				stackCount = context.Values.Count;
			}, (context, result) =>
			{
				Assert.AreEqual(stackCount - 1, context.Values.Count);
				Assert.AreEqual(1, context.Values.Peek());
			});
		}

		[TestMethod]
		public void HandleModWithOnlyOneValueOnTheStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.ModInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
			{
				context.Values.Push(3);
				stackCount = context.Values.Count;
			}, (context, result) =>
			{
				Assert.AreEqual(stackCount, context.Values.Count);
				Assert.AreEqual(0, context.Values.Peek());
			});
		}

		[TestMethod]
		public void HandleModWithEmptyStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.ModInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
			{
				stackCount = context.Values.Count;
			}, (context, result) =>
			{
				Assert.AreEqual(stackCount + 1, context.Values.Count);
				Assert.AreEqual(0, context.Values.Peek());
			});
		}

		[TestMethod]
		public void HandleMultiply()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.MultiplyInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
				{
					context.Values.Push(3);
					context.Values.Push(7);
					stackCount = context.Values.Count;
				}, (context, result) =>
				{
					Assert.AreEqual(stackCount - 1, context.Values.Count);
					Assert.AreEqual(21, context.Values.Peek());
				});
		}

		[TestMethod]
		public void HandleMultiplyWithOnlyOneValueOnTheStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.MultiplyInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
				{
					context.Values.Push(3);
					stackCount = context.Values.Count;
				}, (context, result) =>
				{
					Assert.AreEqual(stackCount, context.Values.Count);
					Assert.AreEqual(0, context.Values.Peek());
				});
		}

		[TestMethod]
		public void HandleMultiplyWithEmptyStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.MultiplyInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
				{
					stackCount = context.Values.Count;
				}, (context, result) =>
				{
					Assert.AreEqual(stackCount + 1, context.Values.Count);
					Assert.AreEqual(0, context.Values.Peek());
				});
		}

		[TestMethod]
		public void HandleSubtract()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.SubtractInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
				{
					context.Values.Push(3);
					context.Values.Push(7);
					stackCount = context.Values.Count;
				}, (context, result) =>
				{
					Assert.AreEqual(stackCount - 1, context.Values.Count);
					Assert.AreEqual(-4, context.Values.Peek());
				});
		}

		[TestMethod]
		public void HandleSubtractWithOnlyOneValueOnTheStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.SubtractInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
				{
					context.Values.Push(3);
					stackCount = context.Values.Count;
				}, (context, result) =>
				{
					Assert.AreEqual(stackCount, context.Values.Count);
					Assert.AreEqual(3, context.Values.Peek());
				});
		}

		[TestMethod]
		public void HandleSubtractWithEmptyStack()
		{
			var cells = new List<Cell>() { new Cell(
				new Point(0, 0), MathInstructionHandler.SubtractInstruction) };
			var stackCount = 0;

			InstructionHandlerTests.Run(new MathInstructionHandler(), cells, (context) =>
				{
					stackCount = context.Values.Count;
				}, (context, result) =>
				{
					Assert.AreEqual(stackCount + 1, context.Values.Count);
					Assert.AreEqual(0, context.Values.Peek());
				});
		}
	}
}
